#include "physics_tests.h"
#include "pmodel.h"
#include "3d.h"
#include "input.h"
#include "render.h"

using namespace WtfEngine;
using namespace WtfEngine::PhysicsTests;


RayCast::RayCast():
Functor() {
	// Bind to mouse
	Kernel::GetInstance().GetGameState()->BindEvent(
		new MouseDownEvent(), this);
	Kernel::GetInstance().GetGameState()->BindEvent(
		new MouseMoveEvent(), new ObjFunctor<RayCast>(this, &RayCast::OnMouseMove));
	Kernel::GetInstance().GetGameState()->BindEvent(
		new MouseUpEvent(), new ObjFunctor<RayCast>(this, &RayCast::OnMouseUp));
};

void RayCast::operator ()() {
	tVector vPos;
	MouseDownEvent * pEv = Kernel::GetInstance().GetCurrentEvent<MouseDownEvent>();

	if(!pEv || pEv->getButton() != MOUSE_LEFT)
		return;

	tRay ray = Renderer::GetInstance().GetCamera()->PixelToRay(Renderer::GetInstance().GetScreen(),
		iVector2D(pEv->x(), pEv->y()));

	WorldObject::tRef rObj = PhysicsEngine::GetInstance().RayCast(ray, vPos);
	if(!rObj.isNull()) {
		mrSelectedObj = dynamic_cast<Physical *>(&*rObj);
		if(mrSelectedObj.isNull()) {
			new Projectile(vPos, tVector());
		};
	};
};

void RayCast::OnMouseMove() {
	tVector vPos;
	MouseMoveEvent * pEv = Kernel::GetInstance().GetCurrentEvent<MouseMoveEvent>();

	if(mrSelectedObj.isNull())
		return;

	tRay ray = Renderer::GetInstance().GetCamera()->PixelToRay(Renderer::GetInstance().GetScreen(),
		iVector2D(pEv->x(), pEv->y()));

	if(!PhysicsEngine::GetInstance().RayCast(ray, vPos).isNull()) {
		mrSelectedObj->setActive();
		mrSelectedObj->getBody()->applyCentralForce(FROMVECT((vPos - mrSelectedObj->getPosition()) * 10 / mrSelectedObj->getMassQuot()));
	};
};

void RayCast::OnMouseUp() {
	mrSelectedObj = (Physical *)NULL;
};

/*
PhysicsTests::Vehicle::Vehicle():
WtfEngine::Vehicle(tCreationOptions(Physical::tCreationOptions(), 40, 5, 4), new WtfGraphics::Primitives::Oblong(2.5, 0.5, 4),
				   new PhysicalModels::Oblong("", tVector(2.5, 0.5, 4), 800))
{
	WtfGraphics::GraphicsData::tRef rWheel = new WtfGraphics::Primitives::Cylinder(0.4, 0.25, 8);
	Orientation3D wheelOri(Orientation3D::Rotation(tVector(0, 1, 0), M_PI / 4));

	this->AddWheel(tVector(-1, -0.3, -1.5), rWheel, wheelOri, tVector(1, 0, 0), tVector(-1, 0.2, -1.5), 0.4, true);
	this->AddWheel(tVector(1, -0.3, -1.5), rWheel, wheelOri, tVector(1, 0, 0), tVector(1, 0.2, -1.5), 0.4, true);
	this->AddWheel(tVector(-1, -0.3, 1.5), rWheel, wheelOri, tVector(1, 0, 0), tVector(-1, 0.2, 1.5), 0.4);
	this->AddWheel(tVector(1, -0.3, 1.5), rWheel, wheelOri, tVector(1, 0, 0), tVector(1, 0.2, 1.5), 0.4);

	this->setAngularFactor(0.5);

	//this->setVelocity(tVector(10, 0, 10));

	// Bind to keyboard
	Kernel::GetInstance().GetGameState()->BindEvent(
		new KeyDownEvent(InputKey(InputKey::UP)), new BoundObjFunctor<PhysicsTests::Vehicle, tScalar>(this, &PhysicsTests::Vehicle::Accelerate, 8000));
	Kernel::GetInstance().GetGameState()->BindEvent(
		new KeyUpEvent(InputKey(InputKey::UP)), new BoundObjFunctor<PhysicsTests::Vehicle, tScalar>(this, &PhysicsTests::Vehicle::Accelerate, 0));
	Kernel::GetInstance().GetGameState()->BindEvent(
		new KeyDownEvent(InputKey(InputKey::DOWN)), new BoundObjFunctor<PhysicsTests::Vehicle, tScalar>(this, &PhysicsTests::Vehicle::Brake, 200));
	Kernel::GetInstance().GetGameState()->BindEvent(
		new KeyUpEvent(InputKey(InputKey::DOWN)), new BoundObjFunctor<PhysicsTests::Vehicle, tScalar>(this, &PhysicsTests::Vehicle::Brake, 0));
	Kernel::GetInstance().GetGameState()->BindEvent(
		new KeyDownEvent(InputKey(InputKey::LEFT)), new BoundObjFunctor<PhysicsTests::Vehicle, tScalar>(this, &PhysicsTests::Vehicle::Steer, 0.4));
	Kernel::GetInstance().GetGameState()->BindEvent(
		new KeyUpEvent(InputKey(InputKey::LEFT)), new BoundObjFunctor<PhysicsTests::Vehicle, tScalar>(this, &PhysicsTests::Vehicle::Steer, 0));
	Kernel::GetInstance().GetGameState()->BindEvent(
		new KeyDownEvent(InputKey(InputKey::RIGHT)), new BoundObjFunctor<PhysicsTests::Vehicle, tScalar>(this, &PhysicsTests::Vehicle::Steer, -0.4));
	Kernel::GetInstance().GetGameState()->BindEvent(
		new KeyUpEvent(InputKey(InputKey::RIGHT)), new BoundObjFunctor<PhysicsTests::Vehicle, tScalar>(this, &PhysicsTests::Vehicle::Steer, 0));
	Kernel::GetInstance().GetGameState()->BindEvent(
		new KeyDownEvent(InputKey(' ')), new BoundObjFunctor<PhysicsTests::Vehicle, tScalar>(this, &PhysicsTests::Vehicle::HandBrake, 200));
	Kernel::GetInstance().GetGameState()->BindEvent(
		new KeyUpEvent(InputKey(' ')), new BoundObjFunctor<PhysicsTests::Vehicle, tScalar>(this, &PhysicsTests::Vehicle::HandBrake, 0));
};

void PhysicsTests::Vehicle::Accelerate(tScalar amt) {
	getBody()->setActivationState(ACTIVE_TAG);
	mpRaycastVehicle->applyEngineForce(amt, 2);
	mpRaycastVehicle->applyEngineForce(amt, 3);
};

void PhysicsTests::Vehicle::Brake(tScalar amt) {
	mpRaycastVehicle->setBrake(amt, 0);
	mpRaycastVehicle->setBrake(amt, 1);
};

void PhysicsTests::Vehicle::HandBrake(tScalar amt) {
	mpRaycastVehicle->setBrake(amt, 2);
	mpRaycastVehicle->setBrake(amt, 3);
};

void PhysicsTests::Vehicle::Steer(tScalar amt) {
	mpRaycastVehicle->setSteeringValue(amt, 0);
	mpRaycastVehicle->setSteeringValue(amt, 1);
};
*/

FUNCTOR(ProjectileEvent)
	const ObjectEvent *pEv = Kernel::GetInstance().GetCurrentEvent<ObjectEvent>();
	
	if(pEv){
		//CollisionEvent
		std::clog << "Projectile collision!" << std::endl;
		//new VectorArrow(
		//PhysicsEngine::GetInstance().SetPassive(pEv->getObject<Physical>());
	};
ENDFUNC;

FUNCTOR(FireFunc)
	const MouseDownEvent *pEv = Kernel::GetInstance().GetCurrentEvent<MouseDownEvent>();
	if(pEv && pEv->getButton() == MOUSE_MIDDLE) {
		new Projectile(Renderer::GetInstance().GetCamera()->getPosition(),
			(Renderer::GetInstance().GetCamera()->getDirection()
			- Renderer::GetInstance().GetCamera()->getPosition()) * 0.5);
	};
ENDFUNC;

Projectile::Projectile(const tVector& pos, const tDirection& vel)
	: Physical(Physical::tCreationOptions(), new WtfGraphics::Primitives::Cube(1),
		   new PhysicalModels::Cube("", 0.5))
{
	this->setPosition(pos);
	this->applyImpulse(vel, tVector());
	
	tVector vtmp = tVector(0.0, -1000, 0.0);
	this->applyForce(Force::tRef(new Force(vtmp)));
	
	Kernel::GetInstance().GetGameState()->BindEvent(new ObjectEvent(this), new ProjectileEvent());
};

void Projectile::BindMouseClick(){
	Kernel::GetInstance().GetGameState()->BindEvent(
		new MouseDownEvent(), Functor::tRef(new FireFunc()));
};


/**** Particle model ****/

Particle::Particle(const tVector& pos, const tDirection& vel)
	: Physical(Physical::tCreationOptions(), new WtfGraphics::Primitives::Sphere(1, 8, 8),
		   new PhysicalModels::Point(""), true)
{
	this->setPosition(pos);
	this->setVelocity(vel);

	tVector vtmp = tVector(0.0, -100, 0.0);
	this->applyForce(Force::tRef(new Force(vtmp)));
};

void Particle::TestThem()
{
	int			i;
	tPosition	pos(0, 20, 0);
	tDirection	vel(-5, 0, 0);
	for(i = 0; i < 10; i++){
		new Particle(pos, vel);
		vel[0] += 1;
	};
};



/**** Stacking Test ****/

class TestCube : public Physical
{
public:
	TestCube(tScalar hl, const tVector& pos)
	: Physical(Physical::tCreationOptions(), new WtfGraphics::Primitives::Oblong(hl, hl, hl),
		   new PhysicalModels::Cube("", hl/2))
	{
		this->setPosition(pos);
		tVector vtmp = tVector(0.0, -98, 0.0);
		this->applyForce(Force::tRef(new Force(vtmp)));
	};

	void Update() {};
};

void Stacking::Run()
{
	static int lastTime = 0;
	if(lastTime != ((int)Kernel::GetInstance().GetTime() / 2)){
		lastTime = (int)Kernel::GetInstance().GetTime() / 2;

		new TestCube(1, tVector(10, 20, 0));
	};
};
